package org.tbjd.client.customWidgets;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.tbjd.client.beans.Task;
import org.tbjd.client.constants.NotificationsConstants;
import org.tbjd.client.customWidgets.taskList.TreeGridItemHandle;
import org.tbjd.client.dndControllers.taskList.FlexTableRowDragController;
import org.tbjd.client.dndControllers.taskList.FlexTableRowDropController;
import org.tbjd.client.exceptions.NoUpperDecisionmakerFoundException;
import org.tbjd.client.exceptions.NoUpperMediatorFoundException;
import org.tbjd.client.exceptions.TBJDException;
import org.tbjd.client.interfaces.Decisionmaker;
import org.tbjd.client.interfaces.Displayer;
import org.tbjd.client.interfaces.HasDragController;
import org.tbjd.client.interfaces.WidgetProvider;
import org.tbjd.client.utils.FlexTableUtil;

import com.allen_sauer.gwt.dnd.client.DragController;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Widget;

public class TreeGrid extends Composite implements Displayer, Decisionmaker, HasDragController {

	private AbsolutePanel container = new AbsolutePanel();
	private FlexTable grid = new FlexTable();

	private ArrayList<Widget> columnHeaders = null;

	private FlexTableRowDragController flexTableDragController = null;
	private FlexTableRowDropController dropController = null;

	private WidgetProvider widgetProvider = null;

	private Task selectedTask = null;

	public TreeGrid() {
		container.add(grid);
		initWidget(container);
		grid.setStyleName("treeGrid");
	}

	public TreeGrid(ArrayList<Widget> columnHeaders) {
		this();

		this.columnHeaders = columnHeaders;

		// format column headers
		for (int i = 0; i < this.columnHeaders.size(); i++) {
			Widget widget = this.columnHeaders.get(i);

			grid.setWidget(0, i, widget);
			grid.getCellFormatter().setStyleName(0, i, "entete");
		}

		dropController = new FlexTableRowDropController(grid);
		flexTableDragController = new FlexTableRowDragController(container);
		flexTableDragController.registerDropController(dropController);
		flexTableDragController.setBehaviorDragStartSensitivity(5);
	}

	public void setColumnsWidth(String[] colWidth) throws Exception {
		if (colWidth == null || colWidth.length != grid.getCellCount(0)) {
			throw new Exception("The columns number must match with the gridTable columns number");
		}

		for (int i = 0; i < colWidth.length; i++) {
			grid.getCellFormatter().setWidth(0, i, colWidth[i]);
		}
	}

	public void display() {
	}

	public void display(List<?> updatedBeans) {
		for (Task task : (List<Task>) updatedBeans) {
			
			for (int j = 0; j < columnHeaders.size(); j++) {
				grid.setWidget(task.getLevel() + 1, j, this.widgetProvider.getWidget(task, j));
				
			}

			Widget handle = grid.getWidget(task.getLevel()+1, 1);
			
			if (task.getType() == Task.PHASE) {
				handle.addStyleName("phase");
			} else {
				if (task.getParent() == null) {
					handle.addStyleName("task");
				} else {
					handle.addStyleName("subTask");
				}
			}
			
			// TODO instead of given the 
			flexTableDragController.makeDraggable(grid.getWidget(task.getLevel() + 1, 1), ((TreeGridItemHandle)grid.getWidget(task.getLevel() + 1, 1)).getHandle());
		}
		
		if(selectedTask != null && selectedTask.getType() == Task.PHASE){
			collapsePhase(selectedTask);
		}
	}

	public void decide(Object action) {
		System.out.println(this.getClass().getName() + " decide()");

		if (((Map<?, ?>) action).containsKey(NotificationsConstants.TASKMANAGER_ACTION_treeGridItem_moved)) {
			System.out.println("TreeGrid.decide() TASKMANAGER_ACTION_treeGridItem_moved");
			updateItemsAndBeans(action);

		} else if (((Map<?, ?>) action).containsKey(NotificationsConstants.DRAGGING_TREEGRIDITEM_SOURCE)) {
			Task taskDragged = this.getTaskAtRow(((Map<?, Integer>) action).get(NotificationsConstants.DRAGGING_TREEGRIDITEM_SOURCE));
			Task taskTarget = this.getTaskAtRow(((Map<?, Integer>) action).get(NotificationsConstants.DRAGGING_TREEGRIDITEM_TARGET));

			System.out.println("TreeGrid.decide() DRAGGING_TREEGRIDITEM_SOURCE from " + taskDragged + " to " + taskTarget);

		} else if (((Map<?, ?>) action).containsKey(NotificationsConstants.TREEGRIDITEM_SELECTED)) {
			
			if(selectedTask != null){
				
				if(this.selectedTask.equals(((Map<?, Task>) action).get(NotificationsConstants.TREEGRIDITEM_SELECTED))){
					grid.getRowFormatter().setStyleName(selectedTask.getLevel()+1, "taskList_itemDeselected");
					this.selectedTask = null;
				}else{
					grid.getRowFormatter().setStyleName(selectedTask.getLevel()+1, "taskList_itemDeselected");
					this.selectedTask = ((Map<?, Task>) action).get(NotificationsConstants.TREEGRIDITEM_SELECTED);
					grid.getRowFormatter().setStyleName(selectedTask.getLevel()+1, "taskList_itemSelected");
				}
			}else{
				this.selectedTask = ((Map<?, Task>) action).get(NotificationsConstants.TREEGRIDITEM_SELECTED);
				grid.getRowFormatter().setStyleName(selectedTask.getLevel()+1, "taskList_itemSelected");
			}
			
			try {
				delegate(action);
			} catch (NoUpperDecisionmakerFoundException e) {
				e.printStackTrace();
			} catch (NoUpperMediatorFoundException e) {
				e.printStackTrace();
			}
			
			System.out.println("selected item" + selectedTask);

		} else if (((Map<?, ?>) action).containsKey(NotificationsConstants.TASKLIST_COLLAPSE)) {
			System.out.println("TreeGrid.decide() TASKLIST_COLLAPSE");
			togglePhase(((Map<?, Task>) action).get(NotificationsConstants.TASKLIST_COLLAPSE));
		
		} else if (((Map<?, ?>) action).containsKey(NotificationsConstants.DATEMANAGER_ACTION_duration)) {
			System.out.println("TreeGrid.decide() DATEMANAGER_ACTION_duration");
			try {
				delegate(action);
			} catch (NoUpperDecisionmakerFoundException e) {
				e.printStackTrace();
			} catch (NoUpperMediatorFoundException e) {
				e.printStackTrace();
			}
		}else if (((Map<?, ?>) action).containsKey(NotificationsConstants.DATEMANAGER_ACTION_progress)) {
			System.out.println("TreeGrid.decide() DATEMANAGER_ACTION_progress");
			try {
				delegate(action);
			} catch (NoUpperDecisionmakerFoundException e) {
				e.printStackTrace();
			} catch (NoUpperMediatorFoundException e) {
				e.printStackTrace();
			}
		}else if (((Map<?, ?>) action).containsKey(NotificationsConstants.DATEMANAGER_ACTION_beginDate)) {
			System.out.println("TreeGrid.decide() DATEMANAGER_ACTION_beginDate");
			try {
				delegate(action);
			} catch (NoUpperDecisionmakerFoundException e) {
				e.printStackTrace();
			} catch (NoUpperMediatorFoundException e) {
				e.printStackTrace();
			}
		}else if (((Map<?, ?>) action).containsKey(NotificationsConstants.DATEMANAGER_ACTION_endDate)) {
			System.out.println("TreeGrid.decide() DATEMANAGER_ACTION_endDate");
			try {
				delegate(action);
			} catch (NoUpperDecisionmakerFoundException e) {
				e.printStackTrace();
			} catch (NoUpperMediatorFoundException e) {
				e.printStackTrace();
			}
		}
	}

	public void delegate(Object action) throws NoUpperDecisionmakerFoundException, NoUpperMediatorFoundException {
		System.out.println("TreeGrid.delegate()");
		// passes info to the first responsible
		Widget parent = ((Widget) this).getParent();
		while (parent != null && parent instanceof Decisionmaker == false) {
			parent = ((Widget) parent).getParent();
		}
		if (parent != null) {
			try {
				((Decisionmaker) parent).decide(action);
			} catch (TBJDException e) {
				e.printStackTrace();
			}
		} else {
			throw new NoUpperDecisionmakerFoundException();
		}
	}

	public void handle(Object action) {
	}

	public void setDragController(DragController dragController) {
	}


	public void setItemWidgetProvider(WidgetProvider t) {
		this.widgetProvider = t;
	}

	/**
	 * Return the selected item of the treeGrid. Returns the last item if none selected.
	 * 
	 * @return a TreeGridITem
	 */
	public Task getSelectedItem() {
		return selectedTask;
	}
	
	public int getLastLevel(){
		return grid.getRowCount() - 1;
	}

	public void setSelectedItem(Task selectedItem) {
		this.selectedTask = selectedItem;
	}

	/**
	 * Collapse or expand children of the given phase
	 * 
	 * @param phase
	 *            the phase whose children will be collapsed
	 */
	private void togglePhase(Task phase) {
		System.out.println("TreeGrid.collapsePhase");
		phase.setCollapsed(!phase.isCollapsed());
		collapsePhase(phase);
	}
	
	
	private void collapsePhase(Task phase){
		for (int i = phase.getLevel() + 2; i <= phase.getLevel() + phase.getLstChildren().size() + 1; i++) {
			grid.getRowFormatter().setVisible(i, !phase.isCollapsed());
		}
	}

	/**
	 * Update TreeGridItem(s) information as well as related beans
	 * 
	 * @param action
	 */
	private void updateItemsAndBeans(Object action) {

		//TODO[1] move this method in the TaskManager and add css management
		
		int originPosition = (Integer) ((Map<?, ?>) action).get(NotificationsConstants.TREEGRIDITEM_MOVED_ORIGIN_POSITION);
		int targetPosition = (Integer) ((Map<?, ?>) action).get(NotificationsConstants.TREEGRIDITEM_MOVED_TARGET_POSITION);

		Task taskOrigin = (Task) ((Map<?, ?>) action).get(NotificationsConstants.TASKMANAGER_ACTION_treeGridItemMoved_origin);
		Task taskTarget = (Task) ((Map<?, ?>) action).get(NotificationsConstants.TASKMANAGER_ACTION_treeGridItemMoved_target);

		System.out.println("TreeGrid.updateItemsAndBeans() origin bean=" + taskOrigin.getName());
		System.out.println("TreeGrid.updateItemsAndBeans() target bean=" + taskTarget.getName());

		// taskOrigin is a phase
		if (taskOrigin.getType() == Task.PHASE) {

			// check if the gridItemTarget is a phase, task or subtask
			if (taskTarget.getType() == Task.PHASE) {
				System.out.println("phase dragged into a phase : it's not allowed");

			} else if (taskTarget.getParent() == null) {
				System.out.println("phase dragged into a task : move phase and children (done)");
				System.out.println("origin position " + originPosition);
				System.out.println("target position " + targetPosition);

				for (int i = 0; i < taskOrigin.getLstChildren().size(); i++) {
					FlexTableUtil.moveRow(grid, grid, (originPosition + 1), (targetPosition + 2));
				}

			} else {
				
				//Dragged on a subtask, so we get its level in the phase
				int targetPositionInPhase = taskTarget.getParent().getLstChildren().indexOf(taskTarget);
				
				//Check if taskTarget is the last subTask
				if (taskTarget.getParent().getLstChildren().size() - 1 == targetPositionInPhase) {
					System.out.println("phase dragged into the last subtask : do something");
					for (int i = 0; i < taskOrigin.getLstChildren().size(); i++) {
						FlexTableUtil.moveRow(grid, grid, (originPosition + 1), (targetPosition + 2));
					}

				} else {
					System.out.println("phase dragged into a subtask : not allowed");
				}
			}

		//taskOrigin is a task
		} else if (taskOrigin.getParent() == null) {

			//taskTarget is a phase
			if (taskTarget.getType() == Task.PHASE) {
				System.out.println("task dragged into a phase : (done)");
				taskTarget.getLstChildren().add(0, taskOrigin);
				taskOrigin.setParent(taskTarget);
				
			//taskTarget is a task
			} else if (taskTarget.getParent() == null) {
				System.out.println("task dragged into a task : (done)");
				
			//taskTarget is a subTask
			} else {
				System.out.println("task dragged into a subtask : (done)");
				int positionInChildrenList = taskTarget.getParent().getLstChildren().indexOf(taskTarget);
				taskTarget.getParent().getLstChildren().add(positionInChildrenList + 1, taskOrigin);
				taskOrigin.setParent(taskTarget.getParent());
			}
		
		//taskOrigin is a subTask
		} else {
			detach(taskOrigin);

			//taskTarget is a phase
			if (taskTarget.getType() == Task.PHASE) {
				System.out.println("subtask dragged into a phase : (done)");
				taskOrigin.getParent().getLstChildren().remove(taskOrigin);
				taskTarget.getLstChildren().add(0, taskOrigin);
				taskOrigin.setParent(taskTarget);
			//taskTarget is a task
			} else if (taskTarget.getParent() == null) {
				System.out.println("subtask dragged into a task : (done)");
				taskOrigin.setParent(null);
			//taskTarget is a subTask
			} else {
				System.out.println("subtask dragged into a subtask : (done)");
				int positionInChildrenList = taskTarget.getParent().getLstChildren().indexOf(taskTarget);
				taskOrigin.getParent().getLstChildren().remove(taskOrigin);
				taskTarget.getParent().getLstChildren().add(positionInChildrenList + 1, taskOrigin);

				taskOrigin.setParent(taskTarget.getParent());
				System.out.println("subtask dragged " + taskOrigin.getName());

			}
		}
	}

	

	/**
	 * detach a gridItem from its previous parent
	 * 
	 * @param gridItem
	 */
	private void detach(Task task) {
		task.getLstChildren().remove(task);
	}

	/**
	 * Return the TreeGridItem corresponding to the given rowPosition
	 * 
	 * @param position
	 *            row position to retrieve the task from
	 * @return the task or null if not found
	 */
	private Task getTaskAtRow(int rowPosition) {
		return (Task) ((TreeGridItemHandle) grid.getWidget(rowPosition, 1)).getTask();
	}

	/**
	 * 
	 * @param integer
	 */
	public void deleteRows(Integer numberOfRowsToDelete) {
		
		int rowStart = grid.getRowCount()-1;
		
		for(int i = 0; i < numberOfRowsToDelete; i++){
			grid.removeRow(rowStart - i);
		}
	}

	public void resetSelectedItem() {
		grid.getRowFormatter().setStyleName(this.selectedTask.getLevel()+1, "taskList_itemDeselected");
		this.selectedTask = null;
	}
	
}